require("Link")

Door = class("Door", Link)

Door.LockedStep = 1
Door.OpenedStep = 2

---

function Door:initialize(name, container1, container2, closedAndLocked)

	Link.initialize(self, name, container1, container2, Door.OpenedStep, "Door")

	
	-- create links inside locations 
	
	if container1.subContainers[container2.name] then
	
		container1.doorsToContainedLocations[name] = self
		container2.doorsToContainingLocations[name] = self
	else
		container1.doorsBetweenContainedLocations[name] = self
		container1.doorsToNeighborhood[name] = self
		container2.doorsToNeighborhood[name] = self
	end
	
	self.noLock = not closedAndLocked
	
	if self.noLock
	then
		self:enable()	
	end
end

---

function Door:observe(observedLocation)

	if self:hasUndeterminedSate() then
	
		
		-- deal lock filtering
		
		gLogger:enterGroup("Check and filter possible dead locks when observing & collapsing door " .. self.name)
		
		if self:isOpenableFromThisSide(observedLocation) then
		
			Link.observe(self, observedLocation)		-- if door is openable from the observed side, we can colase it regularly because even if it collaspe closed, it will not create a dead lock
			
			if not self:isEnabled() then
			
				-- remove possible locations of the related Key that is outside of the accesible space 
				-- remaining after collapsing the door closed
			
				local accesibleLocations = observedLocation:getAccessibleLocations(true)
				local relatedKey = gField.entityTypes.Key[self.name]
				
				for potentialLocation, probability in pairs(relatedKey.CurrentContainer) do
				
					if 
						(not accesibleLocations[potentialLocation.name]) and
						(potentialLocation:isAKindOf("Location"))	
					then
						gLogger:log("Remove location " .. potentialLocation.name .. " from key " .. relatedKey.name)
						relatedKey.CurrentContainer[potentialLocation] = nil
					end
				end
				
				assert(next(relatedKey.CurrentContainer), "No possible Location remaining for Key " .. relatedKey.name)
			end
		else
			gLogger:log("Collapse the door open to avoid dead lock : " .. self.name)
			
			self:enable()						-- if it's not openable from this side, we collaspe it open to avoid any dead lock
		end
		
		gLogger:leaveGroup()
	end
end

---

function Door:isOpenableFromThisSide(observedLocation, onlyUseCollapsedKeys, recursiveCall)

	local isOpenable
	local neededKey = gField.entityTypes.Key[self.name]

	gLogger:enterGroup("Check if Door " .. self.name .. " is openable from this side : " .. observedLocation.name .. "  Needed key : " .. neededKey.name, true)


	-- prepare recusivity loops prevention
	
	if not recursiveCall then
	
		gLogger:log("Clear recursivity loop prevention for Door : " .. self.name)
		
		Door.alreadySearchingKey = {}
		Door.alreadyAccessibleKey = {}
	end
	
		
	-- ckeck if the player has the key

	if gPlayer.body:havePropInPockets(neededKey) then

		gLogger:log("The needed key is found in the player pocket " .. neededKey.name)
		
		isOpenable = true
		
	elseif Door.alreadySearchingKey[neededKey] then
			
		gLogger:log("Was already searching the same key.  This key is already acessible : " .. tostring(self.alreadyAccessibleKey[neededKey]))
		
		isOpenable = Door.alreadyAccessibleKey[neededKey]
	else
		Door.alreadySearchingKey[neededKey] = true
		Door.alreadyAccessibleKey[neededKey] = false			-- false before recursivity
		
		isOpenable = neededKey:isAccessibleFrom(observedLocation, onlyUseCollapsedKeys)
		
		Door.alreadyAccessibleKey[neededKey] = isOpenable
	end
	
	if not recursiveCall then
	
		Door.alreadySearchingKey = nil
		Door.alreadyAccessibleKey = nil
	end
	
	if isOpenable then
	
		gLogger:log("Door " .. self.name .. " is openable from this side : " .. observedLocation.name)
	else
		gLogger:log("Door " .. self.name .. " is not openable from this side : " .. observedLocation.name)
	end
	
	gLogger:leaveGroup()
	
	return isOpenable
end


